﻿/*
 * Catalyst - http://catalystobjects.com
 * 
 * New BSD License (BSD)
 * 
 * Copyright (c) 2009, Ashley Brener
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 * 
 * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 * 
 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
 * 
 * Neither the name of Midlet Earth nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.Collections.Generic;
using System.Data;
using System.Xml.Serialization;
using System.Web;
using System.Web.Caching;
using Catalyst.Caching;
using SubSonic;

namespace Catalyst
{
	public abstract class BusinessObject<ItemType> : ActiveRecord<ItemType>, IBusinessObject, IDataRelational
		where ItemType : BusinessObject<ItemType>, new()
	{
		internal MappingList mappingList;

		private MappingList getMappings()
		{
			if (mappingList == null)
			{
				mappingList = GetMappings();

				if (mappingList == null)
				{
					throw new Exception(String.Format("No mappings found for BusinessObject {0}", GetType().FullName));
				}
			}

			return mappingList;
		}

		#region IBusinessObject Members

		public abstract MappingList GetMappings();

		private bool useLazyLoading = true;
		[XmlIgnore]
		public bool UseLazyLoading { get { return useLazyLoading; } set { useLazyLoading = value; } }

		#endregion

		#region IDataRelational Members

		public abstract TableSchema.Table GetTableSchema();

		#endregion

		private List<string> relationNamesToLoad;

		//public abstract bool IsLoaded { get; }
	
		#region *** Schema ***

		private DataProvider provider;

		public TableSchema.Table CreateTableSchema(string ProviderName, string TableName)
		{
			if (provider == null)
			{
				provider = DataService.Providers[ProviderName];
			}

			return provider.GetTableSchema(TableName, getTableType(TableName));
		}

		private TableType getTableType(string tableName)
		{
			List<string> viewNames = new List<string>(provider.GetViewNameList());

			if (viewNames.Contains(tableName))
			{
				return TableType.View;
			}
			
			return TableType.Table;
		}

		#endregion

		#region *** Load Data ***

		protected internal void Load(TableSchema.Table TableSchema, DataTable DataTable)
		{
			BaseSchema = TableSchema;

			Load(DataTable);
		}

		protected internal void Load(TableSchema.Table TableSchema, IDataReader Reader)
		{
			BaseSchema = TableSchema;

			Load(Reader);
		}

		protected internal void LoadAndCloseReader(TableSchema.Table TableSchema, IDataReader Reader)
		{
			BaseSchema = TableSchema;

			LoadAndCloseReader(Reader);
		}

		public void Load(ItemType Item)
		{
			BaseSchema = Item.GetSchema();

			object oVal = null;

			foreach (KeyValuePair<string, Mapping> pair in Item.getMappings())
			{
				oVal = Item.GetObjectValue(pair.Value.Name);

				if (oVal != null)
				{
					SetValue(pair.Value.Name, oVal);
				}
			}

			IsLoaded = true;
		}

		public void Load(TableSchema.TableColumnSettingCollection ColumnSettings)
		{
			BaseSchema = GetTableSchema();

			foreach (TableSchema.TableColumnSetting setting in ColumnSettings)
			{
				SetColumnValue(setting.ColumnName, setting.CurrentValue);
			}

			IsLoaded = true;
		}

		//internal abstract void LoadInternal(IDataReader Reader);
		#endregion

		#region *** RelationalObject Loading ***

		//private string getAllCacheKey(TableSchema.Table tableSchema, Type type)
		//{
		//    return String.Format("{0}_All{1}_{2}", tableSchema.Provider.Name, tableSchema.Name, type.Name);
		//}

		//private CacheSettings<List<ItemType>> getAllCachedListItems(params TableSchema.Table[] tableSchemas)
		//{
		//    TableSchema.Table tableSchema = tableSchemas[0];

		//    CacheSettings<List<ItemType>> settings = new CacheSettings<List<ItemType>>();

		//    DataTable dataTable = getAllCachedDataTable(tableSchemas).CachedObject;

		//    ItemType businessObject = null;

		//    List<ItemType> list = new List<ItemType>();

		//    foreach (DataRow row in dataTable.Rows)
		//    {
		//        businessObject = new ItemType();
		//        businessObject.Load(tableSchema, row);
		//        list.Add(businessObject);
		//    }

		//    settings.CachedObject = list;

		//    return settings;
		//}


		//private CacheSettings<DataTable> getAllCachedDataTable(params TableSchema.Table[] tableSchemas)
		//{
		//    TableSchema.Table tableSchema = tableSchemas[0];

		//    CacheSettings<DataTable> settings = new CacheSettings<DataTable>();

		//    SqlCacheDependency dependency = null;

		//    if (HttpContext.Current != null)
		//    {
		//        HashSet<string> cacheEnabledTables = new HashSet<string>(SqlCacheDependencyAdmin.GetTablesEnabledForNotifications(tableSchema.Provider.DefaultConnectionString));
		//        foreach (string enabledTable in cacheEnabledTables)
		//        {
		//            if (enabledTable == tableSchema.TableName)
		//            {
		//                dependency = new SqlCacheDependency(tableSchema.Provider.Name, enabledTable);
		//                break;
		//            }
		//        }
		//    }

		//    SqlQuery query = new Select().From(tableSchema);

		//    settings.CachedObject = query.ExecuteDataSet().Tables[0];
		//    settings.Dependency = dependency;

		//    return settings;
		//}

		public void LoadAsRelation(TableSchema.Table RelatedTableSchema, string ForeignKeyName, object PrimaryKeyValue, HashSet<Aggregate> Aggregates)
		{
			BaseSchema = RelatedTableSchema;

			//bool loadFromCache = false;

			//if (HttpContext.Current != null)
			//{
			//    HashSet<string> cacheEnabledTables = new HashSet<string>(SqlCacheDependencyAdmin.GetTablesEnabledForNotifications(BaseSchema.Provider.DefaultConnectionString));
			//    foreach (string enabledTable in cacheEnabledTables)
			//    {
			//        if (enabledTable == BaseSchema.TableName)
			//        {
			//            loadFromCache = true;
			//            break;
			//        }
			//    }
			//}

			//if (loadFromCache)
			//{
			//    DataTable allItems = CachingService.GetCachedObject<DataTable, TableSchema.Table>(CacheHelper.GetCacheKey(BaseSchema, typeof(DataTable)), new CachingService.ObjectLoader<DataTable, TableSchema.Table>(CacheHelper.GetAllCachedDataTable), new TableSchema.Table[] { BaseSchema });

			//    DataTable clonedItems = Cloning.CloneHelper<DataTable>.Clone(allItems);

			//    foreach (DataRow row in clonedItems.Rows)
			//    {
			//        if (row[ForeignKeyName].Equals(PrimaryKeyValue))
			//        {
			//            Load(BaseSchema, row);

			//            break;
			//        }
			//    }
			//}
			//else
			//{
			//    LoadAsRelation<IDataRelational>(RelatedTableSchema, ForeignKeyName, PrimaryKeyValue, Aggregates);
			//}

			DataTable allData = CachedDataLoader.GetCachedData(new CachedDataLoader.DataRequest(BaseSchema));

			foreach (DataRow row in allData.Rows)
			{
				if (row[ForeignKeyName].Equals(PrimaryKeyValue))
				{
					Load(BaseSchema, row);

					break;
				}
			}
		}

		//public T LoadAsRelation<T>(TableSchema.Table RelatedTableSchema, string ForeignKeyName, object PrimaryKeyValue)
		//{
		//    return LoadAsRelation<T>(RelatedTableSchema, ForeignKeyName, PrimaryKeyValue, null);
		//}

		//protected internal T LoadAsRelation<T>(TableSchema.Table RelatedTableSchema, string ForeignKeyName, object PrimaryKeyValue, HashSet<Aggregate> Aggregates)
		//{
		//    BaseSchema = RelatedTableSchema;

		//    SqlQuery query = new Select()
		//    .From(BaseSchema)
		//    .Where(ForeignKeyName).IsEqualTo(PrimaryKeyValue);

		//    if (Aggregates != null && Aggregates.Count > 0)
		//    {
		//        foreach (Aggregate aggregate in Aggregates)
		//        {
		//            query.And(aggregate);
		//        }
		//    }

		//    LoadAndCloseReader(query.ExecuteReader());

		//    return (T)Convert.ChangeType(this, GetType());
		//}

		#endregion


		public void LoadRelations(IList<string> RelationNames)
		{
			if (RelationNames != null && RelationNames.Count > 0)
			{
				relationNamesToLoad = new List<string>(RelationNames);

				string relationTableName = null;
				string fkName = null;
				string pkName = null;

				MappingList mappings = getMappings();
				DataRelationMapping mapping = null;

				if (mappings != null)
				{
					foreach (string name in relationNamesToLoad)
					{
						// Case sensitive
						try
						{
							if (mappings[name] != null && (!UseLazyLoading || !checkIfLoaded(name)))
							{
								if (mappings[name] is DataRelationMapping && mappings[name].Value is IDataRelational)
								{
									mapping = (DataRelationMapping)mappings[name];

									TableSchema.Table relationTable = mapping.RelatedTable.Provider.GetTableSchema(mapping.RelatedTable.TableName, mapping.RelatedTable.TableType);

									relationTableName = relationTable.TableName;

									pkName = GetPrimaryKeyName();
									//fkName = getPrimaryKeyTables()[relationTableName];

									foreach (TableSchema.PrimaryKeyTable pkt in table.PrimaryKeyTables)
									{
										if (pkt.Name == relationTableName)
										{
											fkName = pkt.ColumnName;
										}
									}

									((IDataRelational)mapping.Value).LoadAsRelation(relationTable, fkName, GetColumnValue<int>(pkName), mapping.Aggregates);

									if (mapping.Value != null)
									{
										if (UseLazyLoading)
										{
											flagObjectAsLoaded(name);
										}

										IDataRelational relationalObject = mapping.Value as IDataRelational;

										if (relationalObject != null)
										{
											relationalObject.LoadRelations(relationNamesToLoad);
										}
									}

									break;
								}
							}
						}
						catch (KeyNotFoundException ex) { }
					}
				}
			}
		}

		#region *** REASSESS THIS ***

		private HashSet<string> loadedObjectValues;

		private bool checkIfLoaded(string objectName)
		{
			if (useLazyLoading)
			{
				if (loadedObjectValues == null)
				{
					loadedObjectValues = new HashSet<string>();
				}

				foreach (string s in loadedObjectValues)
				{
					if (s == objectName.ToLower())
					{
						return true;
					}

				}
			}

			return false;
		}

		#endregion

		private void flagObjectAsLoaded(string objectName)
		{
			if (useLazyLoading && !loadedObjectValues.Contains(objectName.ToLower()))
			{
				loadedObjectValues.Add(objectName.ToLower());
			}
		}

		public string GetPrimaryKeyName()
		{
		    return table.PrimaryKey.ColumnName;
		}

		//private Dictionary<string, string> pkTables;

		//private Dictionary<string, string> getPrimaryKeyTables()
		//{
		//    if (pkTables == null)
		//    {
		//        pkTables = new Dictionary<string, string>();

		//        //string relationshipName = null;

		//        foreach (TableSchema.PrimaryKeyTable pkt in table.PrimaryKeyTables)
		//        {
		//            //relationshipName = String.Format("{0}_{1}", pkt.Name, pkt.ColumnName);

		//            //pkTables.Add(relationshipName, pkt.ColumnName);
		//            pkTables.Add(pkt.Name, pkt.ColumnName);
		//        }
		//    }

		//    return pkTables;
		//}

		//private Dictionary<string, TableSchema.ForeignKeyTable> fkTables;

		//private Dictionary<string, TableSchema.ForeignKeyTable> getForeignKeyTables()
		//{
		//    if (fkTables == null)
		//    {
		//        fkTables = new Dictionary<string, TableSchema.ForeignKeyTable>();

		//        foreach (TableSchema.ForeignKeyTable fkt in table.ForeignKeys)
		//        {
		//            fkTables.Add(fkt.Name, fkt);
		//        }
		//    }

		//    return fkTables;
		//}

		//public DbType GetDBType(string DataColumnName)
		//{
		//    return table.GetColumn(DataColumnName).DataType;
		//}

		

		//private bool checkIfRequiredToLoad(string relationName)
		//{
		//    if (relationNamesToLoad != null)
		//    {
		//        if (relationNamesToLoad.Count > 0 && relationNamesToLoad.Contains(relationName))
		//        {
		//            return true;
		//        }
		//    }
		//    else
		//    {
		//        return true;
		//    }

		//    return false;
		//}

		//private void ensureFullTableSchema()
		//{
		//    if (table.PrimaryKeyTables.Count == 0 && table.ForeignKeys.Count == 0)
		//    {
		//        BaseSchema = table.Provider.GetTableSchema(table.TableName, table.TableType);
		//    }
		//}

		private CacheSettings<TableSchema.Table> getCachedFullTableSchema()
		{
			CacheSettings<TableSchema.Table> settings = new CacheSettings<TableSchema.Table>();

			TableSchema.Table tbl = null;

			if (table.PrimaryKeyTables.Count == 0 && table.ForeignKeys.Count == 0)
			{
				tbl = table.Provider.GetTableSchema(table.TableName, table.TableType);
			}
			else
			{
				tbl = table;
			}

			settings.CachedObject = tbl;
			settings.Priority = CacheItemPriority.High;
			settings.SlidingExpiration = TimeSpan.FromDays(7);

			return settings;
		}

		private CacheSettings<Dictionary<string, TableSchema.Table>> getCachedFullTableSchemas()
		{
			CacheSettings<Dictionary<string, TableSchema.Table>> settings = new CacheSettings<Dictionary<string, TableSchema.Table>>();

			Dictionary<string, TableSchema.Table> schemas = new Dictionary<string, TableSchema.Table>();

			settings.CachedObject = new Dictionary<string, TableSchema.Table>();
			settings.Priority = CacheItemPriority.High;
			settings.SlidingExpiration = TimeSpan.FromDays(7);

			return settings;
		}

		private TableSchema.Table getFullTableSchema()
		{
			//string cacheKey = String.Format("FullTableSchema_{0}", table.TableName);
			
			//return CachingService.GetCachedObject<TableSchema.Table>(cacheKey, new CachingService.ObjectLoader<TableSchema.Table>(getCachedFullTableSchema));

			Dictionary<string, TableSchema.Table> fullTableSchemas = CachingService.GetCachedObject<Dictionary<string, TableSchema.Table>>("FullTableSchemas", new CachingService.ObjectLoader<Dictionary<string, TableSchema.Table>>(getCachedFullTableSchemas));

			if (!fullTableSchemas.ContainsKey(table.TableName))
			{
				TableSchema.Table tbl = null;

				if (table.PrimaryKeyTables.Count == 0 && table.ForeignKeys.Count == 0)
				{
					tbl = table.Provider.GetTableSchema(table.TableName, table.TableType);
				}
				else
				{
					tbl = table;
				}

				fullTableSchemas.Add(table.TableName, tbl);
			}

			return fullTableSchemas[table.TableName];
		}

		public object GetObjectValue(string Name)
		{
			if (table != null)
			{
				MappingList mappings = getMappings();

				if (mappings.ContainsKey(Name))
				{
					Mapping mapping = mappings[Name];

					if (mapping != null && (!UseLazyLoading || !mapping.IsLoaded))// || (mapping.Value is IBusinessObjectList)))
					{
						if (mapping is DataRelationMapping && mapping.Value is IDataRelational)
						{
							DataRelationMapping dataRelationMapping = (DataRelationMapping)mapping;

							TableSchema.Table relationTable = dataRelationMapping.RelatedTable.Provider.GetTableSchema(dataRelationMapping.RelatedTable.TableName, dataRelationMapping.RelatedTable.TableType);
							
							string relationTableName = relationTable.TableName;

							string pkName = null;
							string fkName = null;

							bool isFound = false;

							object pkValue = null;

							//ensureFullTableSchema();

							TableSchema.Table fullTableSchema = getFullTableSchema();

							// Check if relation is related to primary key
							foreach (TableSchema.PrimaryKeyTable pkt in fullTableSchema.PrimaryKeyTables)
							{
								if (pkt.Name == relationTableName)
								{
									if (!String.IsNullOrEmpty(dataRelationMapping.ForeignKeyColumnName))
									{
									    pkName = dataRelationMapping.ForeignKeyColumnName;
									}
									else
									{
										pkName = GetPrimaryKeyName();
									}

									fkName = pkt.ColumnName;

									isFound = true;

									break;
								}
							}

							if (!isFound)
							{
								// Check if relation is related to a foreign key
								foreach (TableSchema.ForeignKeyTable fkt in fullTableSchema.ForeignKeys)
								{
									if (fkt.Name == relationTableName)
									{
										pkName = fkt.ColumnName;
										fkName = fkt.PrimaryColumnName;

										isFound = true;
										
										break;
									}
								}
							}

							if (isFound)
							{
								try
								{
									pkValue = GetColumnValue(pkName);
								}
								catch (ArgumentException ex) { }
							}
							else
							{
								// Check if relation is many to many

								foreach (TableSchema.PrimaryKeyTable thisPKT in fullTableSchema.PrimaryKeyTables)
								{
									if (isFound)
									{
										break;
									}

									foreach (TableSchema.PrimaryKeyTable relatedPKT in relationTable.PrimaryKeyTables)
									{
										if (thisPKT.Name == relatedPKT.Name)
										{
											// Campaigns
											TableSchema.Table manyToManyTable = relatedPKT.Provider.GetTableSchema(relatedPKT.TableName, relatedPKT.TableType);

											DataTable allData = CachedDataLoader.GetCachedData(new CachedDataLoader.DataRequest(manyToManyTable));

											if (allData.Rows.Count > 0)
											{
												DataRow row = allData.Rows[0];

												if (row[thisPKT.ColumnName].Equals(GetColumnValue(GetPrimaryKeyName())))
												{
													pkValue = row[relatedPKT.ColumnName];
													fkName = relationTable.PrimaryKey.ColumnName;
												}
											}

											isFound = true;

											break;
										}
									}
								}
							}

							if (!isFound)
							{
								throw new Exception(String.Format("No relationship defined in database between tables \"{0}\" and \"{1}\".", table.TableName, relationTableName));
							}


							//getPrimaryKeyTables();

							//if (pkTables.ContainsKey(relationTableName))
							//{
							//    if (!String.IsNullOrEmpty(dataRelationMapping.ForeignKeyColumnName))
							//    {
							//        pkName = dataRelationMapping.ForeignKeyColumnName;
							//    }
							//    else
							//    {
							//        pkName = getPrimaryKeyName();
							//    }

							//    fkName = pkTables[relationTableName];
							//}
							//else
							//{
							//    getForeignKeyTables();

							//    if (fkTables.ContainsKey(relationTableName))
							//    {
							//        pkName = fkTables[relationTableName].ColumnName;
							//        fkName = fkTables[relationTableName].PrimaryColumnName;
							//    }
							//}

							if (pkValue != null)
							{
								((IDataRelational)mapping.Value).LoadAsRelation(relationTable, fkName, pkValue, dataRelationMapping.Aggregates);

								if (mapping.Value is IBusinessObject)
								{
									if (((IBusinessObject)mapping.Value).IsLoaded)
									{
										mapping.IsLoaded = true;
									}
								}
								else if (mapping.Value is IBusinessObjectList)
								{
									if (((IBusinessObjectList)mapping.Value).Count > 0)
									{
										mapping.IsLoaded = true;
									}
								}
							}
						}
						else if (mapping is DataMapping)
						{
							DataMapping dataMapping = (DataMapping)mapping;

							if (!String.IsNullOrEmpty(dataMapping.ColumnName))
							{
								object newObj = null;

								try
								{
									newObj = GetColumnValue<object>(dataMapping.ColumnName);
								}
								catch
								{
									//throw new Exception(String.Format("No data exists in column \"{0}\" for MappingName \"{1}\" in BusinessObject \"{2}\"", dataMapping.ColumnName, Name, GetType().FullName));
								}

								if (newObj != null)
								{
									if (mapping.Type.IsEnum)
									{
										mapping.Value = Enum.Parse(dataMapping.Type, newObj.ToString());
									}
									else
									{
										mapping.Value = newObj;
									}
								}
								else if (mapping.Type != typeof(object))
								{
									// Get a default value

									switch (mapping.Type.Name)
									{
										case "Boolean": mapping.Value = default(Boolean); break;
										default: break;
									}
								}

								if (mapping.Value != null)
								{
									mapping.IsLoaded = true;
								}
							}
						}
						else
						{
							throw new Exception(String.Format("Mapping \"{0}\" cannot be an abstract Mapping", Name));
						}
					}

					return mapping.Value;
				}
				
				throw new Exception(String.Format("Mapping \"{0}\" does not exist in MappingList", Name));
			}

			return null;
		}

		public T GetObjectValue<T>(string Name)
		{
			object obj = GetObjectValue(Name);

			if (obj != null)
			{
				Tools tools = new Tools();
				return tools.ConvertType<T>(obj);
			}

			return default(T);
		}

		public void SetValue(string Name, object Obj)
		{
			if (table == null)
			{
				BaseSchema = GetTableSchema();
			}

			if (table != null)
			{
				MappingList mappings = getMappings();

				bool isFound = false;

				foreach (KeyValuePair<string, Mapping> item in mappings)
				{
					if (item.Key.ToLower() == Name.ToLower())
					{
						item.Value.Value = Obj;

						isFound = true;

						if (item.Value is DataMapping)
						{
							DataMapping dataMapping = (DataMapping)item.Value;

							SetColumnValue(dataMapping.ColumnName, dataMapping.Value);
						}
						else if (item.Value is DataRelationMapping)
						{
							DataRelationMapping dataRelationMapping = (DataRelationMapping)item.Value;

							//TableSchema.Table relationTable = dataRelationMapping.RelatedTable.Provider.GetTableSchema(dataRelationMapping.RelatedTable.TableName, dataRelationMapping.RelatedTable.TableType);

							string relationTableName = dataRelationMapping.RelatedTable.TableName;// relationTable.TableName;

							string foreignKeyColumnName = null;

							//ensureFullTableSchema();

							TableSchema.Table fullTableSchema = getFullTableSchema();

							foreach (TableSchema.ForeignKeyTable fkt in fullTableSchema.ForeignKeys)
							{
								if (fkt.Name == relationTableName)
								{
									foreignKeyColumnName = fkt.ColumnName;

									break;
								}
							}

							object oVal = null;

							foreach (TableSchema.TableColumn column in table.Columns)
							{
								if (column.ColumnName == foreignKeyColumnName)
								{
									try
									{
										if (dataRelationMapping.Value is IBusinessObject)
										{
											oVal = ((IBusinessObject)dataRelationMapping.Value).GetPrimaryKeyValue();
										}
										else
										{
											oVal = dataRelationMapping.Value;
										}

										SetColumnValue(foreignKeyColumnName, oVal);
									}
									catch (NullReferenceException ex) { }

									break;
								}
							}
						}

						break;
					}
				}

				if (!isFound)
				{
					throw new Exception(String.Format("Mapping \"{0}\" does not exist in MappingList", Name));
				}
			}
		}

		///// <summary>
		///// Saves this object's state to the underlying data source.
		///// </summary>
		//public void Save()
		//{
		//    Save(String.Empty);
		//}

		///// <summary>
		///// Saves this object's state to the underlying data source.
		///// </summary>
		///// <param name="userID">The user ID.</param>
		//public void Save(int userID)
		//{
		//    Save(userID.ToString());
		//}

		///// <summary>
		///// Saves this object's state to the underlying data source.
		///// </summary>
		///// <param name="userID">The user ID.</param>
		//public void Save(Guid userID)
		//{
		//    string sUserID = userID.ToString();
		//    Save(sUserID);
		//}

		//public void Save()
		//{
		//    if (Schema != null)
		//    {
		//        foreach (Mapping mapping in getMappings())
		//        {
		//            if (mapping is DataMapping)
		//            {
		//                DataMapping dm = (DataMapping)mapping;

		//                SetDataValue(dm.ColumnName, dm.Value);
		//            }
		//            else if (mapping is DataRelationMapping)
		//            {
		//                DataRelationMapping drm = (DataRelationMapping)mapping;

		//                if (drm.Value is RelationalObject)
		//                {
		//                    RelationalObject ro = (RelationalObject)drm.Value;
		//                    this.Set
		//                }


		//            }



					
		//        }
		//    }
		//}

		//public DT GetDataValue<DT>(string ColumnName)
		//{
		//    DT oOut = default(DT);

		//    if (columnSettings != null)
		//    {
		//        oOut = columnSettings.GetValue<DT>(ColumnName);
		//    }
			
		//    return oOut;
		//}

		//public object GetDataValue(string ColumnName)
		//{
		//    object oOut = null;

		//    if (columnSettings != null)
		//    {
		//        oOut = columnSettings.GetValue(ColumnName);
		//    }

		//    return oOut;
		//}

		//public void SetDataValue(string ColumnName, object Obj)
		//{
		//    if (columnSettings != null)
		//    {
		//        columnSettings.SetValue(ColumnName, Obj);
		//    }
		//}

		//public bool CheckDataExists(string DataColumnName)
		//{
		//    string dataColumnName = DataColumnName.ToLower();

		//    if (columnSettings != null
		//        && columnSettings.Contains(dataColumnName)
		//        && columnSettings[dataColumnName] != null
		//        && columnSettings[dataColumnName].CurrentValue != DBNull.Value)
		//    {
		//        return true;
		//    }

		//    return false;
		//}

		

		//public DataObject<ItemType> Load(IDataReader Reader)
		//{
		//    Schema = GetTableSchema();
		//    columnSettings = new TableSchema.TableColumnSettingCollection();

		//    try
		//    {
		//        if (Reader.Read())
		//        {
		//            foreach (TableSchema.TableColumn column in Schema.Columns)
		//            {
		//                columnSettings.Add(new TableSchema.TableColumnSetting(column.ColumnName.ToLower(), Reader[column.ColumnName]));
		//            }
		//        }
		//    }
		//    finally
		//    {
		//        if (Reader != null)
		//        {
		//            Reader.Close();
		//        }
		//    }

		//    return this;
		//}

		public void Load(Dictionary<string, object> fields)
		{
			Load(GetTableSchema(), fields);
		}

		protected internal void Load(TableSchema.Table TableSchema, Dictionary<string, object> fields)
		{
			BaseSchema = TableSchema;
			//BaseSchema = GetTableSchema();
			//columnSettings = new TableSchema.TableColumnSettingCollection();

			foreach (KeyValuePair<string, object> field in fields)
			{
				SetColumnValue(field.Key.ToLower(), field.Value);
				//columnSettings.Add(new TableSchema.TableColumnSetting(field.Key.ToLower(), field.Value));
			}

			//return this;
		}

		public override void Load(DataRow dr)
		{
			Load(GetTableSchema(), dr);
		}

		public void Load(TableSchema.Table TableSchema, DataRow Row)
		{
			BaseSchema = TableSchema;

			foreach(TableSchema.TableColumn col in BaseSchema.Columns)
            {
				SetColumnValue(col.ColumnName, Row[col.ColumnName]);
			}

            SetLoadState();
        }

		public virtual void Save()
		{
			Save(String.Empty);
		}

		public virtual void Save(int userID)
		{
			Save(userID.ToString());
		}

		public virtual void Save(Guid userID)
		{
			string sUserID = userID.ToString();
			Save(sUserID);
		}

		public virtual void Save(string userName)
		{
			MappingList mappings = getMappings();

			foreach (TableSchema.TableColumn column in BaseSchema.Columns)
			{
				bool columnExistsInMappings = false;

				foreach (KeyValuePair<string, Mapping> item in mappings)
				{
					if (item.Value is DataMapping && item.Value.Value != null)
					{
						DataMapping dataMapping = (DataMapping)item.Value;

						if (dataMapping.ColumnName == column.ColumnName)
						{
							columnExistsInMappings = true;
						}
					}
					else if (item.Value is DataRelationMapping)
					{
						DataRelationMapping dataRelationMapping = (DataRelationMapping)item.Value;
						
						if (dataRelationMapping.Value is IBusinessObject)
						{
							if (dataRelationMapping.Value != null)
							{
								IBusinessObject businessObject = (IBusinessObject)dataRelationMapping.Value;

								foreach (TableSchema.ForeignKeyTable fkt in table.ForeignKeys)
								{
									if (fkt.Name == dataRelationMapping.RelatedTable.TableName && fkt.ColumnName == column.ColumnName && businessObject.IsLoaded)
									{
										columnExistsInMappings = true;
										break;
									}
								}
							}
						}
						else if (dataRelationMapping.Value is IBusinessObjectList)
						{
							IBusinessObjectList businessObjects = (IBusinessObjectList)dataRelationMapping.Value;

							businessObjects.Save(userName);
						}
					}
				}

				if (!columnExistsInMappings)
				{
					//column.MaxLength = 0;
					//column.AutoIncrement = false;
					//column.IsNullable = true;
					//column.IsPrimaryKey = false;
					//column.IsForeignKey = false;
					//column.IsReadOnly = false;
					//column.DefaultSetting = @"";
					//column.ForeignKeyTableName = "";

					if (column.ColumnName != GetPrimaryKeyName())
					{
						SetColumnValue(column.ColumnName, DBNull.Value);
					}
				}
			}

			base.Save(userName);
		}

		//public DataObject<ItemType> Load(DataRow Row)
		//{
		//    Schema = GetTableSchema();
		//    columnSettings = new TableSchema.TableColumnSettingCollection();

		//    foreach (DataColumn column in Row.Table.Columns)
		//    {
		//        columnSettings.Add(new TableSchema.TableColumnSetting(column.ColumnName.ToLower(), Row[column]));
		//    }

		//    return this;
		//}

		//public DataObject<ItemType> Load(ItemType Item)
		//{
		//    Schema = GetTableSchema();
		//    columnSettings = Item.columnSettings;

		//    return this;
		//}

		////internal BusinessObject<ItemType> LoadFromCache<ListType>(int Id)
		////{
		////    LoadSchema();
		////    columnSettings = ColumnSettings;

		////    return this;
		////}

		//internal DataObject<ItemType> Load(TableSchema.TableColumnSettingCollection ColumnSettings)
		//{
		//    Schema = GetTableSchema();
		//    columnSettings = ColumnSettings;

		//    return this;
		//}

		/// <summary>
		/// Returns BusinessObject as a Relation from a cached BusinessObjectList
		/// </summary>
		/// <typeparam name="ItemType">Item of type BusinessObject</typeparam>
		/// <typeparam name="ListType">List of type BusinessObjectList</typeparam>
		/// <param name="ForeignKeyName"></param>
		/// <param name="PrimaryKeyValue"></param>
		/// <returns></returns>
		//public DataObject<ItemType> LoadAsRelationFromCache<ListType>(string ForeignKeyName, object PrimaryKeyValue)
		//    where ListType : DataObjectList<ItemType, ListType>, new()
		//{
		//    ListType businessObjects = new ListType();
		//    businessObjects.LoadAll();
			
		//    foreach (ItemType item in businessObjects)
		//    {
		//        if (item.GetDataValue(ForeignKeyName).Equals(PrimaryKeyValue))
		//        {
		//            loadInternal(item.columnSettings);

		//            return this;
		//        }
		//    }

		//    return this;
		//}

		public void LoadRelations()
		{
			LoadRelations(getMappings().Names);
		}

		//#region IEnumerable<ItemType> Members

		//public IEnumerator<ItemType> GetEnumerator()
		//{
		//    throw new NotImplementedException();
		//}

		//#endregion

		
	}
}
